package cn.learn.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicLong;

public class ClientCnxnSocketNetty {
    private final EventLoopGroup eventLoopGroup;
    protected final ByteBuffer lenBuffer = ByteBuffer.allocateDirect(4);
    private ChannelFuture connectFuture;

    protected boolean initialized = false;
    protected final AtomicLong recvCount = new AtomicLong(0L);
    protected ByteBuffer incomingBuffer = lenBuffer;

    public ClientCnxnSocketNetty() {
        eventLoopGroup = new NioEventLoopGroup(1);
    }

    void connect(InetSocketAddress addr) throws IOException {
        Bootstrap bootstrap = new Bootstrap().group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SO_LINGER, -1)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ZKClientPipelineFactory(addr.getHostString(), addr.getPort()));

        connectFuture = bootstrap.connect(addr);
    }

    public void seed() {
        try {
            // 消息长度，4字节
            int xid = 2;
            int type = 1;
            String path = "/zoo";
            int conlen = path.getBytes(StandardCharsets.UTF_8).length;
            byte[] data = "超帅的".getBytes(StandardCharsets.UTF_8);

            ByteBuffer bb;

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dataOutputStream = new DataOutputStream(baos);
            dataOutputStream.writeInt(-1);  // 先占位
            dataOutputStream.writeInt(xid);  // xid
            dataOutputStream.writeInt(type);  // type
            dataOutputStream.writeInt(conlen);  // conlen
            dataOutputStream.write(path.getBytes(StandardCharsets.UTF_8));   // path
            dataOutputStream.writeInt(data.length);  // conlen
            dataOutputStream.write(data);   // path

            baos.close();

            bb = ByteBuffer.wrap(baos.toByteArray());
            bb.putInt(bb.capacity() - 4);
            bb.rewind();

            final ByteBuf writeBuffer = Unpooled.wrappedBuffer(bb);

            System.out.println(writeBuffer);

            connectFuture.sync().channel().writeAndFlush(writeBuffer);
            // 等待直到连接中断
//            connectFuture.channel().closeFuture().sync();
        } catch (InterruptedException | IOException e) {
            e.printStackTrace();
        }
    }

    private class ZKClientPipelineFactory extends ChannelInitializer<SocketChannel> {
        private String host;
        private int port;

        public ZKClientPipelineFactory(String host, int port) {
            this.host = host;
            this.port = port;
        }

        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            pipeline.addLast("handler", new ZKClientHandler());
        }
    }

    private class ZKClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
            while (buf.isReadable()) {
                if (incomingBuffer.remaining() > buf.readableBytes()) {
                    int newLimit = incomingBuffer.position() + buf.readableBytes();
                    incomingBuffer.limit(newLimit);
                }
                String s = buf.toString(StandardCharsets.UTF_8);
                System.out.println(s);
                buf.clear();
                /*buf.readBytes(incomingBuffer);
                incomingBuffer.limit(incomingBuffer.capacity());
                if (!incomingBuffer.hasRemaining()) {
                    incomingBuffer.flip();
                    if (incomingBuffer == lenBuffer) {
                        recvCount.getAndIncrement();
                        readLength();
                    } else if (!initialized) {
                        readConnectResult();
                        lenBuffer.clear();
                        incomingBuffer = lenBuffer;
                        initialized = true;
//                        updateLastHeard();
                    } else {
//                        sendThread.readResponse(incomingBuffer);
                        lenBuffer.clear();
                        incomingBuffer = lenBuffer;
//                        updateLastHeard();
                    }
                }*/
            }
        }
    }

    void readLength() throws IOException {
        int len = incomingBuffer.getInt();
        if (len < 0 || len > 0xfffff) {
            throw new IOException("Packet len " + len + " is out of range!");
        }
        incomingBuffer = ByteBuffer.allocate(len);
    }

    void readConnectResult() throws IOException {
        System.out.println("返回数据了" + incomingBuffer);
    }

    public static byte[] toHH(int n) {
        byte[] b = new byte[4];
        b[3] = (byte) (n & 0xff);
        b[2] = (byte) (n >> 8 & 0xff);
        b[1] = (byte) (n >> 16 & 0xff);
        b[0] = (byte) (n >> 24 & 0xff);
        return b;
    }
}
